package demo.game.chess.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;

import demo.game.chess.client.ui.AbstractColorTheme.EnumColorStyle;
import demo.game.chess.common.AbstractChessPiece;
import demo.game.chess.common.ChessCell;
import demo.game.chess.common.ChessGameManager;
import demo.game.chess.common.ChessMove;
import demo.game.chess.common.EnumChessColor;
import demo.game.chess.common.MoveHistory;
import demo.game.chess.common.Position;

/**
 * User interface for the game.
 * 
 * @author Prawee Sriplakich
 *
 */
public class ChessBoardUI extends SimplePanel {

	private AbstractIconTheme iconTheme = new ClassicIconTheme();

	private AbstractColorTheme colorTheme = new BlueColorTheme();

	//private static final int IMAGE_HEIGHT = 40;
	private static final int CELL_SIZE = 60;

	private static final int LABEL_CELL_SIZE = 20;

	private static final int GRID_SIZE = 8;

	private final PickupDragController dragController;

	private final ChessMoveValidatorUI moveValidator;

	private final AbstractChessUI mainUI;

	/**
	 * if , then the user plays the black side.
	 */
	private final EnumChessColor playerColor;

	private final FlexTable flexTable = new FlexTable();

	/**
	 * used for drag and drop control.
	 */
	private final AbsolutePanel boardPanel = new AbsolutePanel();

	private final Map<AbstractChessPiece, ChessPieceUI> mapPieceUI = new HashMap<AbstractChessPiece, ChessPieceUI>();

	private final List<ChessPieceDropController> dropControllers = new ArrayList<ChessPieceDropController>();

	public ChessBoardUI(final AbstractChessUI mainUI,
			final EnumChessColor playerColor) {

		this.mainUI = mainUI;
		this.playerColor = playerColor;

		this.moveValidator = new ChessMoveValidatorUI(mainUI);

		setWidget(this.boardPanel);

		this.boardPanel.addStyleName("boardPanel");
		this.boardPanel.add(this.flexTable);
		int boardSize = CELL_SIZE * GRID_SIZE + LABEL_CELL_SIZE * 2;
		this.boardPanel.setPixelSize(boardSize, boardSize);

		this.dragController = new PickupDragController(this.boardPanel, false);
		this.dragController.setBehaviorMultipleSelection(false);

		this.flexTable.setCellSpacing(0);
		this.flexTable.setCellPadding(0);

		// create a grid (chess board)
		for (int i = 0; i < GRID_SIZE; i++) {
			for (int j = 0; j < GRID_SIZE; j++) {
				// create a simple panel drop target for the current cell
				ChessCellUI cellUI = new ChessCellUI(this, new ChessCell(i, j));

				Position flextablePos = getFlexTablePositon(i, j);

				this.flexTable.setWidget(flextablePos.getRow(), flextablePos
						.getColumn(), cellUI);

				this.dropControllers.add(new ChessPieceDropController(this,
						cellUI));

			}
		}

		//addAllDropControllers();

		createLabels();
		createAllPieceUIs();

		setIconTheme(this.iconTheme);
		setColorTheme(this.colorTheme);
	}

	public void confirmMove(final ChessMove move) {

		getManager().getCurrentBoard().confirmMove(move);
		refreshBoard();
	}

	private void createAllPieceUIs() {
		for (int i = 0; i < GRID_SIZE; i++) {
			for (int j = 0; j < GRID_SIZE; j++) {
				AbstractChessPiece p = getManager().getCurrentBoard()
						.getPieceAt(i, j);

				if (null != p) {
					ChessPieceUI pieceUI = createPieceUI(p);
					ChessCellUI cell = getCellUI(i, j);
					cell.setChessPiece(pieceUI);
					//					Log.debug("add piece :" + img.getTitle() + " " + i + " "
					//							+ j);
				}

			}
		}
	}

	private Label createColumnLabel(final int columnIndex) {
		String label = CellLabel.getColumnLabel(columnIndex);
		Label cell = new Label(label);
		//cell.setPixelSize(CELL_SIZE, LABEL_CELL_SIZE);

		return cell;
	}

	private void createLabels() {
		for (int i = 0; i < GRID_SIZE; i++) {
			Position pos = this.getFlexTablePositon(i, 0);
			this.flexTable.setWidget(pos.getRow(), 0, createRowLabel(i));

			this.flexTable.setWidget(pos.getRow(), GRID_SIZE + 1,
					createRowLabel(i));

		}
		for (int j = 0; j < GRID_SIZE; j++) {
			Position pos = this.getFlexTablePositon(0, j);
			this.flexTable.setWidget(0, pos.getColumn(), createColumnLabel(j));

			this.flexTable.setWidget(GRID_SIZE + 1, pos.getColumn(),
					createColumnLabel(j));

		}
	}

	/**
	 * 
	 *
	 * @return
	 */
	private ChessPieceUI createPieceUI(final AbstractChessPiece p) {
		ChessPieceUI ui = new ChessPieceUI(this, p);

		ui.setTitle(p.getType().toString() + "-" + p.getColor());
		//ui.setHeight(IMAGE_HEIGHT + "px");
		this.mapPieceUI.put(p, ui);
		this.dragController.makeDraggable(ui);
		return ui;

	}

	private Label createRowLabel(final int rowIndex) {
		String label = CellLabel.getRowLabel(rowIndex);
		Label cell = new Label(label);
		cell.setPixelSize(LABEL_CELL_SIZE, LABEL_CELL_SIZE);
		return cell;
	}

	/**
	 * Get the cell according to standard chess cell location
	 * (the white user's view).
	 */
	public ChessCellUI getCellUI(final int i, final int j) {
		Position flextablePos = getFlexTablePositon(i, j);
		try {
			return (ChessCellUI) this.flexTable.getWidget(
					flextablePos.getRow(), flextablePos.getColumn());
		} catch (ClassCastException e) {
			Log.error(e + " i=" + i + " j=" + j + " physical pos: "
					+ flextablePos.getRow() + "," + flextablePos.getColumn());
			throw e;
		}
	}

	public AbstractColorTheme getColorTheme() {
		return this.colorTheme;
	}

	/**
	 * Locate cell in the flextable cell.
	 */
	public Position getFlexTablePositon(final int i, final int j) {
		if (this.playerColor == EnumChessColor.white) {
			// extra row, column for showing row, column. 
			return new Position(i + 1, j + 1);
		} else {
			//
			return new Position((7 - i) + 1, (7 - j) + 1);
		}
	}

	public AbstractIconTheme getIconTheme() {

		return this.iconTheme;
	}

	public AbstractChessUI getMainUI() {
		return this.mainUI;
	}

	public ChessGameManager getManager() {
		return this.mainUI.getManager();
	}

	public ChessMoveValidatorUI getMoveValidator() {
		return this.moveValidator;
	}

	public ChessCellUI locate(final ChessPieceUI chessPieceUI) {

		ChessCell cell = this.mainUI.getManager().getCurrentBoard().locate(
				chessPieceUI.getPiece());
		if (null == cell) {
			return null;
		}
		return getCellUI(cell.getRow(), cell.getColumn());
	}

	private void refreshBoard() {
		//		Log.debug(getClass() +" refresh board");
		for (int i = 0; i < GRID_SIZE; i++) {
			for (int j = 0; j < GRID_SIZE; j++) {
				ChessCellUI cellUI = getCellUI(i, j);

				cellUI.clearHighlightStyles();
				AbstractChessPiece piece = getManager().getCurrentBoard()
						.getPieceAt(i, j);
				//				Log.debug("refreshBoard begin: currentCell=" + cellUI
				//						+ " should-be=" + piece);

				ChessPieceUI currentPieceUI = cellUI.getChessPiece();

				//				Log.debug(getClass() + " refresh : " + new Position(i, j) + " "
				//						+ piece);
				if (null == piece && null != currentPieceUI) {
					if (null != cellUI.getWidget()) {
						//						Log.debug(getClass()
						//								+ " refreshBoard removing piece at" + cellUI);
						cellUI.clearChessPiece();
						cellUI
								.addHighlightStyle(EnumColorStyle.chessCellLastMoveOrigin);
					}
				} else if (null != piece) {

					ChessPieceUI newPieceUI = this.mapPieceUI.get(piece);

					if (null == newPieceUI) {
						// pawn promotion.
						Log.debug(getClass() + " refreshBoard creating piece "
								+ newPieceUI + " for " + cellUI.getCell());
						newPieceUI = createPieceUI(piece);
					}

					if (newPieceUI != currentPieceUI) {
						//						Log.debug(getClass() +" refreshBoard putting piece " + newPieceUI
						//								+ " at " + cellUI.getCell());
						cellUI.setChessPiece(newPieceUI);
						cellUI
								.addHighlightStyle(EnumColorStyle.chessCellLastMoveTarget);

					}
				}

			}
		}

	}

	public void setColorTheme(final AbstractColorTheme colorTheme) {
		this.colorTheme = colorTheme;

		for (int i = 0; i < GRID_SIZE; i++) {
			for (int j = 0; j < GRID_SIZE; j++) {
				ChessCellUI ui = getCellUI(i, j);
				ui.updateTheme();
			}
		}

		for (int i = 0; i < GRID_SIZE; i++) {
			Position pos = this.getFlexTablePositon(i, 0);
			this.flexTable.getWidget(pos.getRow(), 0).setStyleName(
					colorTheme.getColorStyle(EnumColorStyle.rowLabel));
			this.flexTable.getWidget(pos.getRow(), GRID_SIZE + 1).setStyleName(
					colorTheme.getColorStyle(EnumColorStyle.rowLabel));
		}
		for (int j = 0; j < GRID_SIZE; j++) {
			Position pos = this.getFlexTablePositon(0, j);
			this.flexTable.getWidget(0, pos.getColumn()).setStyleName(
					colorTheme.getColorStyle(EnumColorStyle.columnLabel));
			this.flexTable.getWidget(GRID_SIZE + 1, pos.getColumn())
					.setStyleName(
							colorTheme
									.getColorStyle(EnumColorStyle.columnLabel));
		}
	}

	public void setDndSupport(final boolean value) {
		if (value) {
			for (ChessPieceDropController c : this.dropControllers) {
				this.dragController.registerDropController(c);
			}
		} else {
			for (ChessPieceDropController c : this.dropControllers) {
				this.dragController.unregisterDropController(c);
			}
		}

	}

	public void setIconTheme(final AbstractIconTheme theme) {
		this.iconTheme = theme;

		for (ChessPieceUI piece : this.mapPieceUI.values()) {
			this.iconTheme.applyTheme(piece);
		}

	}

	public MoveHistory undoLastMove() {
		MoveHistory h = this.mainUI.getManager().getCurrentBoard()
				.undoLastMove();
		refreshBoard();
		return h;
	}
}
